package com.apes.fn.scm.advancePayment;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSON;
import com.apes.fn.crm.customerExpand.FnCustomerExpand;
import com.apes.fn.scm.retailOrder.model.RetailOrder;
import com.apes.fn.scm.retailOrder.model.RetailOrderPayMethod;
import com.apes.fn.scm.retailOrder.repository.RetailOrderPayMethodRepository;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.esb.processor.idempotent.AntiHeavy;
import com.apes.framework.plugin.esb.processor.idempotent.AntiHeavyRepository;
import com.apes.framework.plugin.outbox.OutboxEventService;
import com.apes.framework.plugin.outbox.OutboxPayloadWrapper;
import com.apes.framework.util.DateUtil;
import com.apes.framework.util.LogicalUtil;
import com.apes.framework.util.MapUtil;
import com.apes.hr.base.model.Employee;
import com.apes.hr.base.repository.EmployeeRepository;
import com.apes.scm.account.account.model.AccountingMethod;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.account.ledger.service.LedgerService;
import com.apes.scm.account.tax.model.Tax;
import com.apes.scm.account.tax.service.TaxService;
import com.apes.scm.invoice.model.Invoice;
import com.apes.scm.invoice.model.InvoiceItem;
import com.apes.scm.invoice.service.InvoiceService;
import com.apes.scm.masterdata.finance.model.BankAccount;
import com.apes.scm.masterdata.finance.repository.BankAccountRepository;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.masterdata.product.model.Product;
import com.apes.scm.masterdata.product.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.swing.*;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wuq
 * @create 2020-03-17 18:38
 */
@Service
public class AdvancePaymentService extends DomainService {

    @Autowired
    private AdvancePaymentRepository advancePaymentRepository;
    @Autowired
    private PartyRoleRepository partyRoleRepository;
    @Autowired
    private EmployeeRepository employeeRepository;
    @Autowired
    private AdvanceInsuranceDiscountsService advanceInsuranceDiscountsService;
    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private InvoiceService invoiceService;
    @Autowired
    private AdvancePaymentDao advancePaymentDao;

    private JSONArray SOURCE_TYPE_LIST = null;

    public AdvancePayment create(SimpleRequest request){
        AdvancePayment advancePayment = request.getO(AdvancePayment.class);
        advancePayment.setState("sent");
        return save(advancePayment);
    }

    /**
     * 保存并保存数据
     * @param request
     * @return
     */
    public AdvancePayment createAndapprove(SimpleRequest request){
        AdvancePayment advancePayment = request.getO(AdvancePayment.class);
        AdvancePayment oldAdvancePayment = advancePaymentRepository.findByOrigin(advancePayment.getOrigin());
        if (Objects.nonNull(oldAdvancePayment)) return oldAdvancePayment;

        // 增加幂等控制，数据库建议唯一索引
        AntiHeavy antiHeavy = new AntiHeavy();
        antiHeavy.setId("advance.payment.createAndapprove:"+ advancePayment.getOrigin());
        SpringManager.getBean(AntiHeavyRepository.class).save(antiHeavy);

        advancePayment.setState("approve");
        advancePayment.setCompany(advancePayment.getDept().getCompany());
        JSONObject obj=request.get("clerk");
        advancePayment.setDateApprove(new Date());

        //金额判断
        advancePayment.bPayItemNotEqualAmount();
        advancePayment.bPaymentNotEqualAmount();
        checkPaymentMethod(advancePayment);     // 收款方式判断

        Employee employee=employeeRepository.findOne(obj.getString("id"));
        if(employee==null){
            throw new RuntimeException("差找不到相关的雇员:"+obj.getString("id"));
        }

        JSONObject json= JSONObject.parseObject(request.getS());
        FnCustomerExpand customerExpand = this.invoke("fn.customerExpand.updateOrInsert", MapUtil.mapper(
                "customerExpand", null, "customer", json.get("customerInfo"), "customerCar", json.get("customerCar")
        ));

        advancePayment.setCustomerExpand(customerExpand);
        PartyRole user = partyRoleRepository.findOne(JpaDsl.toCriteriaByEq("role.id", "Employee", "party.id", employee.getParty().getId())).orElse(null);
        if (user == null) {
            throw new RuntimeException("差找不到相关的雇员");
        }

        advancePayment.setApproveUID(user);
        advancePayment.setSource(advancePayment.getRemark());
        AdvancePayment advancePaymentNew = save(advancePayment);

        // 推送事务发件箱
        pushOutboxMsg(advancePayment);

        // e保养
        checkMaintenance(advancePayment);
        advancePaymentFlowInvoice(advancePayment);
        extracted(advancePaymentNew);//生成待缴款单
        return advancePaymentNew;
    }

    private void testCreateInvoice(SimpleRequest request){
        JSONObject jo = request.getJO();
        String id = jo.getString("id");
        AdvancePayment advancePayment = advancePaymentRepository.findOne(id);
        advancePaymentFlowInvoice(advancePayment);

    }

    /**
     * 定金单生成账单
     * @param advancePayment advancePayment
     */
    protected void advancePaymentFlowInvoice(AdvancePayment advancePayment) {
        if (advancePayment.getCustomerExpand() == null) return;

        advancePayment.getPaymentMethodSet().stream()
                .filter(paymentMethod -> "09".equals(paymentMethod.getPaymentMethodType().getId()))
                .collect(Collectors.groupingBy(AdvancePaymentMethod::getCustomer))
                .forEach((customer, item) -> {
                    /*
                    //检查信用额度
                    item.forEach(i->{
                        if(i.getAmount()>getCurAmount(advancePayment.getCompany(), i.getCustomer()).get("curBalance")){
                            throw new RuntimeException(String.format("公司[%s]与客户[%s]间的信用额度不够，请联系财务处理",advancePayment.getCompany().getId(),i.getCustomer().getId()));
                        }
                    });*/

                    //增加占款未清项
                    SpringManager.getBean(LedgerService.class).charge(advancePayment.getCompany(), advancePayment.getDept(), customer,
                            InventoryMode.IM_OWN, AccountingMethod.AM_PURCHASE,
                            advancePayment.getId(), advancePayment.getClass().getSimpleName(), new Date(), "occupy_unsettled",
                            item.stream().mapToDouble(AdvancePaymentMethod::getAmount).sum());

                    createUnitClientInvoice(advancePayment, item, customer);
                });
    }
    /**
     * 写入单位客户往来账单
     *
     * @param advancePayment 定金单
     */
    private void createUnitClientInvoice(AdvancePayment advancePayment,
                                         List<AdvancePaymentMethod> payMethods,
                                         PartyRole customer) {
        if (advancePayment.getCustomerExpand() == null) return;

        // 账单主表
        Invoice invoice = new Invoice();
        invoice.setCompany(advancePayment.getCompany());
        invoice.setDept(advancePayment.getDept());
        invoice.setContract(null);
        invoice.setPartner(customer);   // 单位客户
        invoice.setScene(advancePayment.getScene());
        invoice.setSource(advancePayment.getId());
        invoice.setSourceBusinessType(AdvancePayment.class.getSimpleName());
        invoice.setInventoryMode(InventoryMode.IM_OWN);             //设置库存模式：自有
        invoice.setAccountingMethod(AccountingMethod.AM_PURCHASE);  // 核算方式：经销
        invoice.setCustomerExpand(advancePayment.getCustomerExpand().getId());
        invoice.setOriginBusinessType(AdvancePayment.class.getSimpleName());
        invoice.setOrigin(advancePayment.getId());

        //挂账金额
        double unitCustomerMoney = payMethods.stream().mapToDouble(AdvancePaymentMethod::getAmount).sum();
        // 账单明细
        Set<InvoiceItem> invoiceItems = new HashSet();

        if (unitCustomerMoney == advancePayment.getAmount()) {
            //全额抵扣 单位客户  定损直赔
            advancePayment.getPaymentItems().forEach(paymentItem -> {
                InvoiceItem invoiceItem = new InvoiceItem();
                invoiceItem.setProduct(paymentItem.getProduct());
                invoiceItem.setPriceUnit(paymentItem.getPriceUnit());
                invoiceItem.setQty(paymentItem.getProductUomQty());
                invoiceItem.setUom(paymentItem.getUom());
                Tax tax = SpringManager.getBean(TaxService.class).getSaleTax(advancePayment.getCompany(), paymentItem.getProduct());

                invoiceItem.setTax(tax);
                invoiceItem.setSourceItemId(paymentItem.getId());
                invoiceItem.setPriceSubtotal(paymentItem.getPriceTotal());    // 商品金额
                invoiceItems.add(invoiceItem);
            });
        } else {
            //获取挂账特殊商品
            Product product = productRepository.findOne("0002256264");
            //普通挂账
            InvoiceItem invoiceItem = new InvoiceItem();
            invoiceItem.setProduct(product);
            invoiceItem.setPriceUnit(unitCustomerMoney);
            invoiceItem.setQty(1);
            invoiceItem.setUom(product.getUom());
            Tax tax = SpringManager.getBean(TaxService.class).getSaleTax(advancePayment.getCompany(), product);
            invoiceItem.setTax(tax);
            invoiceItem.setSourceItemId(advancePayment.getId() );
            invoiceItem.setPriceSubtotal(unitCustomerMoney);    // 商品金额
            invoiceItems.add(invoiceItem);
        }

        invoice.setInvoiceItems(invoiceItems);
        invoiceService.save(invoice);
    }

    private void checkPaymentMethod(AdvancePayment advancePayment){
        // 对收款方式上面的银行账户与银行账号字段处理
        advancePayment.getPaymentMethodSet().stream()
                .filter( payMethod -> "03".equals(payMethod.getPaymentMethodType().getId()) )
                .forEach( payMethod -> {
                    if (StringUtils.isEmpty(payMethod.getAccountNumber()) &&
                            (Objects.isNull(payMethod.getBankAccount()) || StringUtils.isEmpty(payMethod.getBankAccount().getId())) ) {
                        throw new RuntimeException("转账的收款方式中，银行账号与银行账户字段同时为空，请检查");
                    }

                    // 如果银行账户与银行账号同时存在就 continue
                    if (!StringUtils.isEmpty(payMethod.getAccountNumber()) && Objects.nonNull(payMethod.getBankAccount()) && !StringUtils.isEmpty(payMethod.getBankAccount().getId())) return;

                    /** 分别处理银行账号与账户 **/
                    if (Objects.nonNull(payMethod.getBankAccount()) && !StringUtils.isEmpty(payMethod.getBankAccount().getId()) ){
                        // 根据银行账户获取银行账号
                        payMethod.setAccountNumber(payMethod.getBankAccount().getAccountId());
                    }

                    if (!StringUtils.isEmpty(payMethod.getAccountNumber())) {
                        // 根据银行账号获取账户编码
                        BankAccount bankAccount = SpringManager.getBean(BankAccountRepository.class)
                                .findAll(JpaDsl.toCriteriaByEq("accountId", payMethod.getAccountNumber()))
                                .stream().findFirst()
                                .orElseThrow( () -> new RuntimeException("根据银行账号没有找到对应银行的账户信息") );
                        payMethod.setBankAccount(bankAccount);
                    }
                });
    }


    public AdvancePayment save(AdvancePayment advancePayment) {
        return advancePaymentRepository.saveAndFlush(advancePayment);
    }

    public AdvancePayment update(SimpleRequest request){
        AdvancePayment advancePayment = request.getO(AdvancePayment.class);
        return null;
    }

    public String delete(SimpleRequest request) {
        return "删除成功";
    }

    public AdvancePayment approve(SimpleRequest request) {
        AdvancePayment advancePayment = request.getO(AdvancePayment.class);
        advancePayment.setState("approve");
        advancePayment.setDateApprove(new Date());
        advancePayment.setApproveUID(getPerson(request));
        AdvancePayment advancePaymentnew = save(advancePayment);

        extracted(advancePaymentnew);//生成待缴款单
        return advancePaymentnew;
    }

    /**
     * 生成待缴款
     * @param advancePayment
     */
    private void extracted(AdvancePayment advancePayment) {
        List<AdvancePaymentMethod> advancePaymentMethodList = advancePayment.getPaymentMethodSet().stream()
                .filter(advancePaymentMethod -> "09".equals(advancePaymentMethod.getPaymentMethodType().getId()) &&
                        !"C0005998".equals(advancePaymentMethod.getCustomer().getId()) &&
                        (advancePayment.getCompany().getParty().getId() != advancePaymentMethod.getCustomer().getParty().getId() ||
                                !"Company".equals(advancePaymentMethod.getCustomer().getRole().getId())))
                .collect(Collectors.toList());
        if (advancePaymentMethodList.size() > 0) {
            List<Map> mapList = advancePaymentMethodList.stream()
                    .map(paymentMethod ->
                            MapUtil.mapper("amount", paymentMethod.getAmount(),
                                    "paymentType", paymentMethod.getPaymentMethodType().getId(),
                                    "paymentId", paymentMethod.getPaymentMethodType().getPaymentType().getId())
                    ).collect(Collectors.toList());
            this.invoke("to.prepare.storeCashAndSave", JSON.toJSONString((MapUtil.mapper("orderId", advancePayment.getId(),
                    "deptId", advancePayment.getDept().getId(), "sourceBusinessType", "AdvancePayment",
                    "details", mapList))));
        }
    }


    private void pushOutboxMsg(AdvancePayment advancePayment) {
        // 推送计算平台服务费消息
        OutboxPayloadWrapper wrapper = new OutboxPayloadWrapper();
        wrapper.setSubscriber("fn.partnerCheck.calculateAdvancePaymentFee");
        wrapper.setAggregateId(advancePayment.getId());
        wrapper.setPayload(JSON.toJSONString(MapUtil.mapper("id", advancePayment.getId())));
        SpringManager.getBean(OutboxEventService.class).sendSaveEvent(wrapper);
    }


    public void test(SimpleRequest request) {
        String id = request.get("id");
        AdvancePayment advancePayment = advancePaymentRepository.getOne(id);
        checkMaintenance(advancePayment);
    }

    /**
     * 订金单 e保养写入费用单
     * @param advancePayment
     */
    private void checkMaintenance(AdvancePayment advancePayment) {
        // 判断是否为 e 保养服务
        long count = advancePayment.getPaymentItems().stream()
                .filter(advancePaymentItem -> LogicalUtil.in(advancePaymentItem.getProduct().getId(), "0002902247", "0002902250"))
                .count();
        if (count == 0) return;
        if (advancePayment.getInsuranceDiscountsAmt() == 0) throw new RuntimeException("e保养+ 业务的车险优惠金额不能为 0");

        advanceInsuranceDiscountsService.process(advancePayment);
    }

    private boolean updateSourceType(SimpleRequest request){
        JSONObject jo = request.getJO();
        String id = jo.getString("id");
        AdvancePayment one = advancePaymentRepository.findOne(id);
        String type = jo.getString("type");
        one.setSourceType(type);
        return true;
    }
    private Map findSourceType(SimpleRequest request){
        if (SOURCE_TYPE_LIST==null){
            SOURCE_TYPE_LIST = advancePaymentDao.findSourceType(request.getJO());
        }
        return MapUtil.mapper(
                "content",SOURCE_TYPE_LIST,
                "elements",SOURCE_TYPE_LIST.size(),
                "page",0,
                "pages",1,
                "size",20,
                "total",SOURCE_TYPE_LIST.size()
        );
    }
    private Map findUseAdvancePayForRetailOrder(SimpleRequest request){
        JSONObject jo = request.getJO();
        String id = jo.getString("id");
        if (id==null)return null;
        List<RetailOrderPayMethod> all = SpringManager.getBean(RetailOrderPayMethodRepository.class).findAll(JpaDsl.toCriteria("advancePaymentId", "EQ", id));
//        List<@NotNull RetailOrder> retailOrders = all.stream().map(RetailOrderPayMethod::getOrder).distinct().collect(Collectors.toList());
        List<Map> result = all.stream().map(retailOrderPayMethod -> MapUtil.mapper(
                "id", retailOrderPayMethod.getOrder().getId(),
                "dept", retailOrderPayMethod.getOrder().getDept().getName(),
                "carLicense", retailOrderPayMethod.getOrder().getCustomerExpand().getCarLicense(),
                "mobile", retailOrderPayMethod.getOrder().getCustomerExpand().getMobile(),
                "realname", retailOrderPayMethod.getOrder().getCustomerExpand().getRealname(),
                "type", "用订金",
                "payAmount", retailOrderPayMethod.getAmount(),
                "payMethod", retailOrderPayMethod.getPaymentMethodType().getName(),
                "createDate", DateUtil.format(retailOrderPayMethod.getOrder().getCreateDate(), DateUtil.SHOW_DATE_FORMAT),
                "company", retailOrderPayMethod.getOrder().getCompany().getName()
        )).collect(Collectors.toList());
        return MapUtil.mapper(
                "details",result
        );
    }
}
